package iagovsothello.logger;

import iagovsothello.ai.*;
import iagovsothello.ai.heuristics.HeuristicFunctionCorners;
import iagovsothello.ai.heuristics.HeuristicFunctionEdge;
import iagovsothello.ai.heuristics.HeuristicFunctionMobility;
import iagovsothello.ai.heuristics.HeuristicFunctionScore;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class ScrumExperiment extends ExperimentContainer {

	private static final long serialVersionUID = 4315622425738679998L;
	private int rounds;
	private List<Agent> opponents;
	
	public ScrumExperiment(String name, int dimension, int rounds, List<Agent> opponents) 
	{
		super(name, dimension);
		this.rounds = rounds;
		this.opponents = opponents;
		for (Agent p1 : opponents)
		{
			for (Agent p2 : opponents)
			{
				if (!p1.equals(p2))
				{
					add(p1+" VS "+p2,rounds,p1,p2);
				}
			}
		}
	}

	public ScrumExperiment(String name, boolean print, boolean basicPrint, int dimension, int rounds, List<Agent> opponents) 
	{
		super(name, print, basicPrint, dimension);
		this.rounds = rounds;
		this.opponents = opponents;
		for (Agent p1 : opponents)
		{
			for (Agent p2 : opponents)
			{
				if (!p1.equals(p2))
				{
					add(p1+" VS "+p2,rounds,p1,p2);
				}
			}
		}
	}
	
	public static void main(String[] args) 
	{
		LinkedList<Agent> opponents = new LinkedList<Agent>();
		
		opponents.add(new Randomizer(new Random()));
		opponents.add(new Greedy());
		opponents.add(new HMinimax(5,new HeuristicFunctionEdge()));
		opponents.add(new HMinimax(5,new HeuristicFunctionMobility()));
		opponents.add(new HMinimax(5,new HeuristicFunctionCorners()));
		opponents.add(new HABPruning(5,new HeuristicFunctionEdge()));
		opponents.add(new HABPruning(5,new HeuristicFunctionMobility()));
		opponents.add(new HABPruning(5,new HeuristicFunctionCorners()));
		
		ScrumExperiment exp = new ScrumExperiment("TEST",8,10,opponents);
		exp.setThreads(4);
		System.out.println("SIZE: "+exp.list.size());
		exp.start();
	}
	
	public int getTotalVictories(Agent player)
	{
		int victories = 0;
		for (BasicExperiment ex : list)
		{
			victories += ex.getVictories(player);
		}
		return victories;
	}
	
	public int getVictories(Agent player, int playerNumber)
	{
		int victories = 0;
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(player) == playerNumber)
				victories += ex.getVictories(playerNumber);
		}
		return victories;
	}
	
	public int getLosses(Agent player, int playerNumber)
	{
		int losses = 0;
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(player) == playerNumber)
				losses += ex.getLosses(playerNumber);
		}
		return losses;
	}
	
	public int getDraws(Agent player)
	{
		int losses = 0;
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(player) != 0)
				losses += ex.getDraws();
		}
		return losses;
	}
	
	public int[] getMeanBranchingFactor(Agent player, int playerNumber)
	{
		int N = (dimension*dimension-4)/2 + (dimension*dimension-4)%2;
		
		int[] bf = new int[N];
		Arrays.fill(bf, 0);
		int number = 0;
		
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(player) == playerNumber)
			{
				int[] exBf = ex.getMeanBranchingFactor(playerNumber);
				for (int j = 0; j < exBf.length; j++)
				{
					bf[j] += exBf[j];
				}
				number++;
			}
		}
		
		for (int j = 0; j < N; j++)
		{
			bf[j] = bf[j]/number;
		}
		return bf;
	}
	
	public long[] getTotalMeanTimes(Agent player)
	{
		return getMeanTimes(player);
	}
	
	public long[] getMeanTimes(Agent player, int playerNumber)
	{
		int N = (dimension*dimension-4)/2 + (dimension*dimension-4)%2;
		
		long[] times = new long[N];
		Arrays.fill(times, 0);
		int number = 0;
		
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(player) == playerNumber)
			{
				long[] exTimes = ex.getMeanTimes(playerNumber);
				for (int j = 0; j < exTimes.length; j++)
				{
					times[j] += exTimes[j];
				}
				number++;
			}
		}
		
		for (int j = 0; j < N; j++)
		{
			times[j] = times[j]/number;
		}
		return times;
	}
	
	public long getMaxTime(Agent player, int playerNumber)
	{
		long maxTime = Long.MIN_VALUE;
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(player) == playerNumber)
			{
				long exMaxTime = ex.getMaxTime(playerNumber);
				if (exMaxTime > maxTime)
				{
					maxTime = exMaxTime;
				}
			}
		}
		return maxTime;
	}
	
	public long[] getVisitedNodes(Agent player, int playerNumber)
	{
		int N = (dimension*dimension-4)/2 + (dimension*dimension-4)%2;
		
		long[] nodes = new long[N];
		Arrays.fill(nodes, 0);
		int number = 0;
		
		for (BasicExperiment ex : list)
		{
			if (ex.isPlayer(player) == playerNumber)
			{
				long[] exNodes = ex.getVisitedNodes(playerNumber);
				for (int j = 0; j < exNodes.length; j++)
				{
					nodes[j] += exNodes[j];
				}
				number++;
			}
		}
		
		for (int j = 0; j < N; j++)
		{
			nodes[j] = nodes[j]/number;
		}
		return nodes;
	}
	
	protected void end()
	{
		//niente
	}
	
	protected void print()
	{
		System.out.println("\n\n--------FINE ESPERIMENTO: "+getName()+"--------\n");
		for (Agent p : opponents)
		{
			print(p);
		}
	}
	
	private void print(Agent player) 
	{
		int player1Victories = getVictories(player,1);
		int player2Victories = getVictories(player,2);
		int totalVictories = player1Victories + player2Victories;
		int player1Losses = getLosses(player,1);
		int player2Losses = getLosses(player,2);
		int totalLosses = player1Losses + player2Losses;
		int draws = getDraws(player);
		
		int totalRounds = (totalVictories+totalLosses+draws);
		
		long[] meansTotal = getTotalMeanTimes(player);
		long[] meansPlayer1 = getMeanTimes(player,1);
		long[] meansPlayer2 = getMeanTimes(player,2);
		
		int[] branchingFactorPlayer1 = getMeanBranchingFactor(player,1);
		int[] branchingFactorPlayer2 = getMeanBranchingFactor(player,2);
		
		long[] nodesPlayer1 = getVisitedNodes(player,1);
		long[] nodesPlayer2 = getVisitedNodes(player,2);
		
		long mean = getMeanTime(meansTotal);
		long maxTimeTotal = getMaxTime(player);
		long maxTimePlayer1 = getMaxTime(player,1);
		long maxTimePlayer2 = getMaxTime(player,2);
		
		float rating = getRating(totalVictories,mean,meansPlayer2[0],meansPlayer1[0]);
				
		
		System.out.println("soggetto: "+player);
		System.out.println("rounds per ciascuno scontro: "+rounds);
		System.out.println("numero totale di partite: "+ totalRounds);
		
		System.out.println("vittorie totali: "+totalVictories);
		System.out.println("vittorie come giocatore bianco: "+player1Victories);
		System.out.println("vittorie come giocatore nero: "+player2Victories);
		
		System.out.println("sconfitte totali: "+totalLosses);
		System.out.println("sconfitte come giocatore bianco: "+player1Losses);
		System.out.println("sconfitte come giocatore nero: "+player2Losses);
		
		System.out.println("pareggi: "+draws+"\n");
		
		System.out.println("tempo medio totale per il calcolo di una mossa: "+mean+" us");
		System.out.println("tempo medio come giocatore bianco per il calcolo di una mossa: "+getMeanTime(meansPlayer1)+ " us");
		System.out.println("tempo medio come giocatore nero per il calcolo di una mossa: "+getMeanTime(meansPlayer2) + " us"+"\n");
		
		System.out.println("tempo massimo totale per il calcolo di una mossa: "+maxTimeTotal+" us");
		System.out.println("tempo massimo come giocatore bianco per il calcolo di una mossa: "+maxTimePlayer1+" us");
		System.out.println("tempo massimo come giocatore nero per il calcolo di una mossa: "+maxTimePlayer2+" us");
		
		System.out.printf("\nrating: %.2f\n",rating);
		
		System.out.println("\ntempi medi di calcolo per ciascun turno e nodi visitati mediamente:");
//		System.out.println("Iterazione\tTOT\tTurno\tBianco\tTurno\tNero");
		System.out.println("Turno\tTempo (us)\tB.factor\tNodi");
		for (int i = 0, j = 1; i < meansTotal.length; i++, j += 2)
		{
//			System.out.println((i+1)+"\t"+meansTotal[i]+"\t"+((i+1)*2)+"\t"+meansPlayer1[i]+"\t"+((i+1)*2-1)+"\t"+meansPlayer2[i]);
			System.out.println((j)+"\t"+meansPlayer2[i]+"\t"+branchingFactorPlayer2[i]+"\t"+nodesPlayer2[i]);
			System.out.println((j+1)+"\t"+meansPlayer1[i]+"\t"+branchingFactorPlayer1[i]+"\t"+nodesPlayer1[i]);
		}
		System.out.println("\n\n");
	}
}
